package com.application.metroscanner.tools;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;

import com.application.metroscanner.Ride;

public class Classifier {

	private final static int DISTANCE_LIMITE = 31;

	private ArrayList<Ride> values;

	private Classifier() {
		values = new ArrayList<Ride>();
	}

	public static ArrayList<Cluster> classify(ArrayList<String> iValues) {
		// initialize data
		Classifier classifier = new Classifier();
		for (String value : iValues) {
			Ride ride = new Ride(value);
			classifier.values.add(ride);
		}

		Collections.sort(classifier.values);

		int nbClusters = classifier.values.size();
		Cluster clusters[] = new Cluster[nbClusters];
		for (int i = 0; i < nbClusters; ++i) {
			clusters[i] = classifier.new Cluster(classifier.values.get(i));
		}

		boolean found = false;

		while (!found) {
			double minDist = -1;
			int minI = -1;
			int minJ = -1;

			for (int i = 0; i < nbClusters; i++) {
				for (int j = i + 1; j < nbClusters; j++) {
					if (clusters[i] != null && clusters[j] != null) {
						double distance = clusters[i].distanceTo(clusters[j]);
						if (distance < DISTANCE_LIMITE
								&& (minDist < 0 || distance < minDist)) {
							minDist = distance;
							minI = i;
							minJ = j;
						}
					}
				}
			}

			if (minDist >= 0) {
				clusters[minI].merge(clusters[minJ]);
				clusters[minJ] = null;
			} else {
				found = true;
			}
		}

		ArrayList<Cluster> finalClusters = new ArrayList<Cluster>();
		for (int i = 0; i < nbClusters; i++) {
			if (clusters[i] != null)
				finalClusters.add(clusters[i]);
		}

		return finalClusters;
	}

	@SuppressWarnings("serial")
	public class Cluster implements Serializable{
		private ArrayList<Ride> rides;
		private double centroid;
		private Ride firstRide;
		private Ride lastRide;

		public Cluster(Ride iRide) {
			rides = new ArrayList<Ride>();
			rides.add(iRide);
			centroid = iRide.getMinutes() + iRide.getHours() * 60;
			firstRide = iRide;
			lastRide = iRide;
		}

		public void merge(Cluster iCluster) {
			centroid = centroid * (rides.size())
					+ (iCluster.centroid * iCluster.rides.size());
			rides.addAll(iCluster.rides); // add other rides
			centroid /= rides.size(); // compute new average

			if (firstRide.compareTo(iCluster.firstRide) > 0) {
				firstRide = iCluster.firstRide;
			}
			if (firstRide.compareTo(iCluster.lastRide) < 0) {
				lastRide = iCluster.lastRide;
			}
		}

		public double distanceTo(Cluster iCluster) {
			return Math.abs(iCluster.centroid - this.centroid);
		}	
		
		/*
		 * GETTERS & SETTERS
		 */
		public Ride getLastRide() {
			return lastRide;
		}
	
		public double getCentroid() {
			return centroid;
		}
		public Ride getFirstRide() {
			return firstRide;
		}
		public ArrayList<Ride> getRides() {
			return rides;
		}

	}

	// ///////////////////////////////////////////////////////////////////////////////////////////////////////

	/**
	 * Distance Not used yet
	 * 
	 * @author Admin Distance between 2 rides
	 */
	private class Distance implements Comparable<Object> {
		Cluster clusterA;
		Cluster clusterB;

		public Distance(Cluster iClusterA, Cluster iClusterB) {
			clusterA = iClusterA;
			clusterB = iClusterB;
		}

		public double getDistance() {
			return clusterA.distanceTo(clusterB);
		}

		@Override
		public int compareTo(Object iDistance) {
			int res = 0;
			if (getDistance() < ((Distance) iDistance).getDistance())
				res = -1;
			else if (getDistance() < ((Distance) iDistance).getDistance())
				res = 1;

			return res;
		}
	}
}
